#include "stdio.h"
#include"cpu.h"

#include"bus.h"
#ifndef __GNUC__
#pragma warning(disable : 4700)
#endif

__u32 CCPU::arm_shifter_operand2(ARM_INSN_INFO insn_info,bool *p_shifter_carry_out) 
{

	__u32 base_operand2;
	__u32 shifter_operand;
	bool  shifter_carry_out;
	
	union
	{
	__u8 rotate_cnt;
	__u8 shift_cnt;
	};

	ARM_SHIFT_OP shift_op;
	//
	// operand2
	//
	if(!insn_info.insn_fmt0a.sI)
	{
	// Rm SHIFT Imm
		shift_cnt=insn_info.insn_fmt0a.shift_imm;
		base_operand2=regfile.read_register(insn_info.insn_fmt0a.Rm);
		shift_op=(ARM_SHIFT_OP)insn_info.insn_fmt0a.sOp;

		if(shift_op==ARM_SHIFT_ROR
			&&shift_cnt==0)
			shift_op=ARM_SHIFT_RRX;

//<shift_imm> Specifies the shift. This is an immediate value between 1 and 32. 
//(A shift by 32 isencoded by shift_imm == 0.)
		if(shift_op!=ARM_SHIFT_LSL
			&&shift_cnt==0)
			shift_cnt=32;

	}
	else
	{
	// Rm SHIFT Rs
		shift_cnt=regfile.read_register(insn_info.insn_fmt0b.Rs);
		base_operand2=regfile.read_register(insn_info.insn_fmt0a.Rm);
		shift_op=(ARM_SHIFT_OP)insn_info.insn_fmt0b.sOp;

	}

	switch(shift_op)
	{
	case ARM_SHIFT_LSR:
		//if Rs[7:0] == 0 then
		//shifter_operand = Rm
		//shifter_carry_out = C Flag
		//else if Rs[7:0] < 32 then
		//shifter_operand = Rm Logical_Shift_Right Rs[7:0]
		//shifter_carry_out = Rm[Rs[7:0] - 1]
		//else if Rs[7:0] == 32 then
		//shifter_operand = 0
		//shifter_carry_out = Rm[31]
		//else /* Rs[7:0] > 32 */
		//shifter_operand = 0
		//shifter_carry_out = 0

		if(shift_cnt==0)
		{
			shifter_operand=base_operand2;
			shifter_carry_out=0!=TEST_BIT(regfile.prf[CPSR],CPSR_BIT_C);
		}
		if(shift_cnt<32)
		{
			shifter_operand=base_operand2>>shift_cnt;
			shifter_carry_out=0!=TEST_BIT(base_operand2,shift_cnt-1);
		}
		else if(shift_cnt==32)
		{
			shifter_operand=0;
			shifter_carry_out=0!=TEST_BIT(base_operand2,31);
		}
		else
		{
			shifter_operand=0;
			shifter_carry_out=0;
		}


		break;
	case ARM_SHIFT_LSL:
		//if Rs[7:0] == 0 then
		//shifter_operand = Rm
		//shifter_carry_out = C Flag
		//else if Rs[7:0] < 32 then
		//shifter_operand = Rm Logical_Shift_Left Rs[7:0]
		//shifter_carry_out = Rm[32 - Rs[7:0]]
		//else if Rs[7:0] == 32 then
		//shifter_operand = 0
		//shifter_carry_out = Rm[0]
		//else /* Rs[7:0] > 32 */
		//shifter_operand = 0
		//shifter_carry_out = 0
		if(shift_cnt==0)
		{
			shifter_operand=base_operand2;
			shifter_carry_out=0!=TEST_BIT(regfile.prf[CPSR],CPSR_BIT_C);
		}
		if(shift_cnt<32)
		{
			shifter_operand=base_operand2<<shift_cnt;
			shifter_carry_out=0!=TEST_BIT(base_operand2,32-shift_cnt);
		}
		else if(shift_cnt==32)
		{
			shifter_operand=0;
			shifter_carry_out=0!=TEST_BIT(base_operand2,31);
		}
		else
		{
			shifter_operand=0;
			shifter_carry_out=0;
		}


		break;
	case ARM_SHIFT_ASR:
	//if Rs[7:0] == 0 then
	//shifter_operand = Rm
	//shifter_carry_out = C Flag
	//else if Rs[7:0] < 32 then
	//shifter_operand = Rm Arithmetic_Shift_Right Rs[7:0]
	//shifter_carry_out = Rm[Rs[7:0] - 1]
	//else /* Rs[7:0] >= 32 */
	//if Rm[31] == 0 then
	//shifter_operand = 0
	//shifter_carry_out = Rm[31]
	//else /* Rm[31] == 1 */
	//shifter_operand = 0xFFFFFFFF
		if(shift_cnt==0)
		{
			shifter_operand=base_operand2;
			shifter_carry_out=0!=TEST_BIT(regfile.prf[CPSR],CPSR_BIT_C);
		}
		if(shift_cnt<32)
		{
			shifter_operand=((signed)base_operand2)>>shift_cnt;
			shifter_carry_out=0!=TEST_BIT(base_operand2,shift_cnt-1);
		}
		else if(TEST_BIT(base_operand2,31)==0)
		{
			shifter_operand=0;
			shifter_carry_out=0!=TEST_BIT(base_operand2,31);
		}
		else
		{
			shifter_operand=-1;
			shifter_carry_out=0;
		}

		break;
	case ARM_SHIFT_ROR:
		//if Rs[7:0] == 0 then
		//shifter_operand = Rm
		//shifter_carry_out = C Flag
		//else if Rs[4:0] == 0 then
		//shifter_operand = Rm
		//shifter_carry_out = Rm[31]
		//else /* Rs[4:0] > 0 */
		//shifter_operand = Rm Rotate_Right Rs[4:0]
		//shifter_carry_out = Rm[Rs[4:0] - 1]
	
		if(rotate_cnt==0)
		{
			shifter_operand=base_operand2;
			shifter_carry_out=0!=TEST_BIT(regfile.prf[CPSR],CPSR_BIT_C);
		}
		else 
		{

			rotate_cnt&=0x1f;
			if(rotate_cnt==0)
			{
				shifter_operand=base_operand2;
				shifter_carry_out=0!=TEST_BIT(base_operand2,31);
			}
			else 
			{
				__u32 mask;
				mask=(1<<rotate_cnt)-1;	
				shifter_operand=(base_operand2&mask)<<(32-rotate_cnt);
				shifter_operand|=(base_operand2&~mask)>>(rotate_cnt);

				shifter_carry_out=0!=TEST_BIT(shifter_operand,rotate_cnt-1);
			}

		}

		break;

	case ARM_SHIFT_RRX:
		//shifter_operand = (C Flag Logical_Shift_Left 31) OR (Rm Logical_Shift_Right 1)
		//shifter_carry_out = Rm[0]

		shifter_operand =base_operand2>>1;

		if(TEST_BIT(regfile.prf[CPSR],CPSR_BIT_C))
			shifter_operand |=(1<<31);

		shifter_carry_out=0!=TEST_BIT(shifter_operand,0);
		break;
	}

	if(p_shifter_carry_out)
	*p_shifter_carry_out=shifter_carry_out;

	
	return shifter_operand;
	
}
			
			